package org.fixies.database;

import org.fixies.utils.IOUtils;
import org.fixies.utils.Log;

import javax.sql.DataSource;
import java.io.PrintWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;


public class SingleConnectionDataSource implements DataSource {

    private final Connection conn_;

    public SingleConnectionDataSource(String driverClass, String url,
                                      String user, String password) {
        Connection conn = null;
        try {
            Class.forName(driverClass);
            conn = DriverManager.getConnection(url, user, password);
            conn_ = wrap(conn);
        } catch (Exception e) {
            IOUtils.closeQuietly(conn);
            String msg = "driver=[" + driverClass + "] url=[" + url
                    + "] user=[" + "] password=[" + password + "]";
            throw new IllegalArgumentException(msg, e);
        }
    }

    public Connection getConnection() throws SQLException {
        return conn_;
    }


    public Connection getConnection(String username, String password)
            throws SQLException {
        return conn_;
    }


    public PrintWriter getLogWriter() throws SQLException {
        // Not Implemented
        return null;
    }


    public int getLoginTimeout() throws SQLException {
        // Not Implemented
        return 0;
    }


    public void setLogWriter(PrintWriter out) throws SQLException {
        // Not Implemented
    }


    public void setLoginTimeout(int seconds) throws SQLException {
        // Not Implemented
    }


    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        // Not Implemented
        return false;
    }


    public <T> T unwrap(Class<T> iface) throws SQLException {
        // Not Implemented
        return null;
    }

    private Connection wrap(final Connection target) {
        Connection wrapped =
                (Connection) Proxy.newProxyInstance(
                        getClass().getClassLoader(),
                        new Class[]{Connection.class},
                        new InvocationHandler() {
                            public Object invoke(Object proxy, Method method,
                                                 Object[] args) throws Throwable {
                                if (method.getName().equals("close")) {
                                    Log.warn("Close called", new Throwable());
                                    IOUtils.rollbackQuietly(target);
                                    return null;
                                }
                                try {
                                    return method.invoke(target, args);
                                } catch (InvocationTargetException ex) {
                                    throw ex.getTargetException();
                                }
                            }
                        });

        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                try {
                    target.close();
                } catch (Exception ignored) {
                    ignored = null;
                }
            }
        });
        return wrapped;
    }
}
